// Copyright (c) Microsoft Corporation
// All rights reserved

namespace Microsoft.Windows.Controls.Specialized.TextModel
{
    using System;
    using System.Threading;

    /// <summary>
    /// Text Navigator provides text navigation capabilities to a text buffer
    /// </summary>
    public struct TextNavigator
    {
        #region Private Members

        private int _position;
        private ITextBuffer _textBuffer;

        #endregion // Private Members

        /// <summary>
        /// Creates the Text Navigator given the buffer and the current position as an absolute offset
        /// </summary>
        /// <param name="buffer">
        /// The text buffer which the Navigator will wrap on
        /// </param>
        /// <param name="position">
        /// The current position in terms of absolute buffer offset
        /// </param>
        public TextNavigator(ITextBuffer buffer, int position)
        {
            _position = position;
            _textBuffer = buffer;
        }

        /// <summary>
        /// Creates the Text Navigator given the buffer and the current position in line-col co-ordinates
        /// </summary>
        /// <param name="buffer">
        /// The Text Buffer which the navigator will wrap on
        /// </param>
        /// <param name="line">
        /// The current line
        /// </param>
        /// <param name="offset">
        /// Offset in the current line that provides the current position
        /// </param>
        public TextNavigator(ITextBuffer buffer, int line, int offset) : this (buffer, buffer.GetStartOfLine(line) + offset)
        {
        }

        #region Exposed Properties

        /// <summary>
        /// Gets the underlying text buffer
        /// </summary>
        public ITextBuffer TextBuffer
        {
            get
            {
                return _textBuffer;
            }
        }

        /// <summary>
        /// Gets the current position
        /// </summary>
        public int Position
        {
            get
            {
                return _position;
            }
        }

        /// <summary>
        /// Gets the line number of the current position
        /// </summary>
        public int Line
        {
            get
            {
                return _textBuffer.GetLineContaining(_position);
            }
        }

        /// <summary>
        /// Gets the offset in the line that provides the current position
        /// </summary>
        public int LineOffset
        {
            get
            {
                return _textBuffer.GetColumnOffset(_position);
            }
        }

        #endregion // Exposed Properties

        #region Exposed Methods

        /// <summary>
        /// Moves the current point to the next line
        /// </summary>
        /// <returns>
        /// True if the move succeeded. False, otherwise.
        /// </returns>
        public bool MoveToNextLine()
        {
            int newPos = _textBuffer.GetStartOfNextLine(_position);

            if (newPos == -1)
                return false;

            _position = newPos;
            return true;
        }

        /// <summary>
        /// Moves the current point to the previous line
        /// </summary>
        /// <returns>
        /// True if the move succeeded.  False, otherwise.
        /// </returns>
        public bool MoveToPreviousLine()
        {
            int newPos = _textBuffer.GetStartOfPreviousLine(_position);

            if (newPos == -1)
                return false;

            _position = newPos;
            return true;
        }

        /// <summary>
        /// Moves the current point to the start of the current line
        /// </summary>
        public void MoveToBeginningOfLine()
        {
            _position = _textBuffer.GetStartOfLineContaining(_position);
        }

        /// <summary>
        /// Moves the current point to the end of the current line
        /// </summary>
        public void MoveToEndOfLine()
        {
            _position = _textBuffer.GetEndOfLineContaining(_position);
        }

        /// <summary>
        /// Creates a Text Point representing the current position
        /// </summary>
        public TextPoint ToPoint()
        {
            return new TextPoint(_textBuffer, _position);
        }

        /// <summary>
        /// Creates a Text Point representing the current position, that tracks based on the specified 
        /// tracking mode.
        /// </summary>
        public TextPoint ToPoint(TrackingMode trackingMode)
        {
            return new TextPoint(_textBuffer, _position, trackingMode);
        }

        #endregion // Exposed Methods

        #region Static Operator Overloads

        public static implicit operator int(TextNavigator navigator)
        {
            return navigator._position;
        }

        public static bool operator ==(TextNavigator p1, TextNavigator p2)
        {
            return (p1.TextBuffer == p2.TextBuffer) && (p1.Position == p2.Position);
        }

        public static bool operator !=(TextNavigator p1, TextNavigator p2)
        {
            return !(p1 == p2);
        }

        public static bool operator >(TextNavigator p1, TextNavigator p2)
        {
            return p1.Position > p2.Position;
        }

        public static bool operator <(TextNavigator p1, TextNavigator p2)
        {
            return p1.Position < p2.Position;
        }

        public static bool operator >=(TextNavigator p1, TextNavigator p2)
        {
            return p1.Position >= p2.Position;
        }

        public static bool operator <=(TextNavigator p1, TextNavigator p2)
        {
            return p1.Position <= p2.Position;
        }

        public static int Compare(TextNavigator p1, TextNavigator p2)
        {
            return p1.Position - p2.Position;
        }

        public override bool Equals(object obj)
        {
            if (obj == null || (!(obj is TextNavigator)))
                return false;

            return this == (TextNavigator)obj;
        }

        #endregion // Static Operator Overloads

        #region Overrides

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return string.Format("({0},{1})", Line, LineOffset);
        }

        #endregion // Overrides
    }
}
